500万局の詰将棋

やねうらおさんの233万局面の定跡ファイルを最近使わせてもらったのだが、そのとき、彼のブログで5年も前に3手詰めから11手詰めまでの実戦詰将棋局面を500万例ほど生成して、これをテキストファイルを圧縮した形で無償公開されていることに気が付いた。で、 将棋盤に表示できるようなユーティリティーが欲しい旨のコメントをしている読者もいるみたいだし, これも早速自分のサイトのページに応用させてもらおうかと思った次第である。

で、まずは提供されている圧縮ファイルだが、解凍するとmate3.sfen, mate5.sfen, mate7.sfen, mate9.sfen, mate11.sfen の5つのファイルがでてくる。 それぞれテキストファイルであり、中身はsfen 書式で書かれた局面情報が1行1局面で記述されている。

これをweb siteのページにランダム、あるいは手数と行番号を指定して将棋盤のグラフィックスとして表示させたいわけである。

すべてのデータをブラウザに読み込ませるわけにはいかないので、データ自体はサーバー側に持たせ、ブラウザからは手数と行番号を指定してサーバーに問い合わせ、該当するSFEN一行を取得させて将棋盤上の駒として描画させる、という手法で実現させる。 サーバーでのデータ検索を最適化させるためにmysqlデータベースのテーブルにデータを格納して使う。

データテーブルは一つとして、コラムは手数と問題、各手数の問題カウント、および 通し番号として記録するとしてテーブル構成は以下となる。

FieldTypeNullKeydefaultextra
idINTNOPRIMARYnullauto increment
sfenvarchar(120)YESnull
moveintYESMULnull
rowintYESMULnull

このテーブルをまず作っておき、ここにテキストファイルからデータを流し込むわけだ。 で、今度こそPythonを使って見ようとPyCharmを立ち上げ(個人で使う分には無料なんだよね)ブラウザの検索機能で情報を拾いまくりながら、以下のプログラムを作った。

move と counterで検索することになるので、これらのフィールドはインデックスを作成しておく。

20行以上のパイソンを書いたのはおそらく初めてだが、 なるほどユーザーが多いわけだ。

このパイソンスクリプトを走らせてみる。 上のコードでは各行にスラッシュが8個ある、のと、スペースで分解したアレイ素子の因子が4つになる、というのを valid SFENの簡易判定基準とし、それ以外の行はスキップする構造にしてあったのだが、なんとここで10件近くのスキップが発生した。 テキスト行の情報も表示するようになっていたので ファイルをのぞいてみると、sfenが2行くっついた、つまり改行が抜けている個所が何件かあるのが分かった。 プログラムで生成したはずのファイルに何故にこのようなイレギュラリティが発生するのか不思議ではある。 とにかく手作業でこれらの行を分割し、テーブルをいったん削除して再度の挑戦を行った。Pythonのmysql connectorが優秀なのか、パソコンのハードを更新したからなのかはよくわからないが、500万件10分もかからず終わってしまった。

実は500万件なかったのである。mysqlのデータベースが言うには実際には以下の件数の局面が入っていた。ただこれで目くじら立てるのは野暮というものだ。

3手 998,405件
5 手998,827件
7 手999,071件
9 手999,673件
11 手999,998件

サーバーにPHPでAPIポイントをつくり、例えば /api/mate/3/1245というようなHTTPリクエストをサーバーに送れば、3手詰め、1245行目のsfenがjson formatでかえってくるような仕組みを作る。

ブラウザ側のほうはReactとほぼ同じように使えるPREACTをViteとTypeScriptのTool chainを使用しバタバタと書き上げて、出来上がったのがこのページ。 上の数字をもう一度よく見てもらいたいが、 200問くらいの問題集でも一冊全部解くのはなかなか根気がいるのに、いったい誰がこの圧倒的な数の暴力に立ち向かうというのだ?(ちなみに私は高校時代に買った内藤詰将棋200選をまだ全部解いてない)

やねうらおさんのブログにもかいてあるのだが、これは妙手が入った詰将棋とは違って実戦詰将棋的な問題集になる。 ほとんど並べ詰めのようなものもあれば、少し考える必要のあるものもある。 しかしなのだ。 例えば、この局面 ランダムにピックアップしたものだが、

9手詰め 第88,592問目

最初に2二金打ち、同金としてから4二桂成 同玉以下は 尻にと金をすべらせていってからの飛車成りまでの送り詰めで9手と言っても難しい詰めではない。 詰将棋だと言って出されれば級位者でも解ける問題だ。 ただ実戦となるとこの局面で4五の桂馬が5三と3三を封鎖しており、さらに金を捨てて、3一の金をどかせれば尻金で詰む、そして4一と金のとき5二玉と逃げれば6一飛成で早く詰む、と局面の駒配置がよく見えなければ詰めようとも思わないわけで、実際これくらいの局面で3切れのShogi warsでは初段、下手すると2段レベルとの対戦でも手駒の金は温存したいと、初手から4二桂成と角を取ってくる可能性は自分の経験から言ってかなり高い。

だとすると、この類の局面を大量にながめて勘を鍛えておくことは棋力向上の役に立つのかもしれない。

それはさておき、ここまでできたなら将棋エンジンもつけて回答も導きだせるようにしたらどうだと言われそうだが、残念ながらそちらのほうの知見は自分はまったく持っていないのであった。思うにサーバーにエンジンを実装してというかバーチャルマシンを一台、それもパーフォーマンスの高いやつを立ち上げて将棋エンジンを常駐させ、これのAPIをたたいて正解を求めるというようなイメージなんだが、いかにもリソース費用がかかりそうだ。lishogiのサイトなどは実現しているみたいだが、どうしているのだろうか。 そのうちだれかが無料のAPIサービスを提供してくれないかと無想するわけである。

AIにコードを書いてもらって失敗した話

将棋の局面を記述する書式にSFENという記述方式がある。
これは盤上の駒の配置、手番、先手と後手の持ち駒、手数などがコンパクトに文字列にまとめられたもので、Kyokumen.jpの局面検索に使われていたり、将棋エンジンの定跡ファイルにも使われていたりする。

SFENの書式についてはこの記事にうまく説明されている。

今回、YaneuraouさんがMIT Licenseで新たに公開された233万手の定跡ファイルをみたところ、後手番の局面がなく、先手後手盤面を反転して検索する方法がとられている。つまり、 例えば初期画面から後手が3四歩と指した局面が先手番として登録されている。 屋根裏王のエンジンにはflippedBoardというパラメーターがあり、これをOnするだけで対応するよう設計されているのだが、定跡ファイルをブラウザで将棋盤上に表示させる自分のWebページに追加するためには、後手の手番の時に検索をかけるSFENの局面を反転させてサーバーに問い合わせる必要が生じた。サーバーから帰ってくる候補手も同様に、例えば2六歩を8四歩などと反転させる必要がある

最初の局面を反転させる部分、TypeScript関数として用意すると以下のようになる。

 

このコードで何をやっているかというと

1.まずは SFEN文字列をスペースをデリミターとして文字列アレイに分解する。
2.それぞれのアレイ因子を board, turn, hands, moveCountという変数にわりあてる。 ちなみにMoveCountは今回の定跡ファイルに使用するにはゼロに置き換える必要がある。(上のコードでは未反映)
3.board(駒の盤面位置情報)をさらにdelimeter ‘/’ で各列に分解したアレイを生成
4. array.reverse()を使って列順をひっくり返す。上段の列が下段に、下段の列が上段になる。
5.さらに、さらに各列の文字列を一文字づつに分解してアレイを作る。
6.大文字の駒(後手の駒)を小文字(先手の駒)、小文字の駒(先手の駒)を大文字に置換する
7.array.reduce()を使って、+シンボル(成り駒)因子と次の駒の因子を繋げて一つのアレー因子にまとめる

8. これを.reverse()で左右反転
9. 各列のアレイを文字列に変換
10。各列の文字列アレーをを’デリミター/’で接合し、一つの文字列を生成。→flippedBoard
11. 手番もひっくり返す (flippedTurn)
12. 持ち駒を先手と後手入れ替える (pivotHands) この部分、使ったロジックは以下のとおり

B3Ps14pを S14Pb3pとしたい。
まずは大文字小文字を入れ替える
b3p2S14P (flipped)
最初の大文字のインデックスを取ればよいと思えるが、数字がついた場合(2S)となっていた場合はNG
なので文字列を逆転させてみる
P41S2p3b reversed
これで最初の小文字のインデックスを取る。上の例だと5。文字列長さ8なので 8-5=3
secondPart =flipped.slice(0,3) = b3p
firstPart = flipped.slice(3) =2S14P
これを組み立てる
final = firstPart+secondPart = 2S14Pb3p
うまくいっているのでは?

先手の持ち駒だけの場合 例えば 2P
小文字のインデックスはマイナス1 この場合は処理をスキップ
後手の持ち駒だけの場合 例えば14p 逆転文字列での小文字のインデックスは0 この場合も処理をスキップ

13 .flifppedboard flippedTurn flippedHands moveCountを一つの文字列につなぎ合わせ、この値を返す
とまあ、書いてしまえば簡単な作業の連続だが、実はこのコード、最初AIに聞いて提示されたものが動かなくて、修正を入れ始めたら、結局七割がたは書き直してしまった、というものになる。

最初、Brave Brouserの検索窓に ”Given sfen string, write a javascript function that returns flipped sfen” と打ったらあっさり回答をよこしたのでこれをTypeScriptに書き直し、そのまま定跡ファイルに使ってみたのだた、局面検索がまったくヒットしなかった。  コードを解析して分かったことはAIがくれた回答は上記のステップから 7,8そして12の処理が抜けていた。その結果盤面の上下反転はしたが、そのあとが左右反転をしていないため、ミラーイメージになってしまい、さらに持ち駒があった場合は先手と後手が逆のまま。 Webページの仕組みはこのsfenをサーバーに送ってサーバーのデータベースからこのsfenに対応するmoves情報を返してもらい、次の指し手の候補をリストアップするというものなので、ミラーイメージの局面照会にはヒットする候補手はありませんという答えしか返ってこない。

AIはSFENが何たるかをあまり理解できないまま、WEBに落ちている情報をかき集めて使ったようで、プロンプトで、SFENの定義から始め、期待されるアウトプットのイメージをしっかり伝えておかなければだめだったか。

逆に良いねと思ったのはアレイやオブジェクトの関数をチェイニングして記述する、いわゆるFunctionalな書き方になっていてスマートである。 これは真似したい。

とにかく、AIに仕事をさせたら、今の時点では検証が大事なわけだが、それなら最初から提供されたコードは参考までに留めておいて、実際に使うコードは自分で書いたほうが楽しいかもね(仕事でプログラミングしている人はこうは言えまい)。

追記:Yaneuraouさんからflipsfenをpythonで作成した時の顛末のリンクを教えていただいた。 https://yaneuraou.yaneu.com/2023/12/15/chatgpt-wrote-a-program-to-flip-a-shogi-board/  同じようなところでハマっているんだ、と可笑しくなった半面。 ちょっと待て、これは2年前の記事。 これを読んでいたらこんなに時間を使わなくてもよかったじゃん とも思った次第。

6年ぶりにパソコンを組みたててみた。(Ryzen 5 9600X)

前回自作のパソコンを組んだのは2019年の9月ごろだからそろそろ6年が経過した。 この6年前から使っているパソコンはスペック的にはRyzen5の第2世代(Ryzen 5 2600)で、最初は15ギガのメモリ実装だったのを途中で32ギガバイトまで増やした。 マザーボード(Mobo)はAsusのROG B450‐F Gamingというものだ。 もともと2013年に組み立てたAsRockのMoboを使ったパソコンの調子が6年目で悪くなり、CMOS Batteryの交換やBios Resetなどではらちがあかなかったので、これはコンデンサあたりがダメになったかと見切りをつけ、マザーボードだけ入れ替えたものだ。ケースやパワーサプライ、Sata-SSDなどはその時からの部品を使いまわし、グラフィックカード(Radeon RX 480)は2016年購入のもの、。 もうひと昔前のスペックになるわけだが、Windows 11も動くし、特に不満があるわけでもない。 このAM4ソケットのMoboはまだファームウエアの更新が活発だし、M2規格のnVMEもサポートしているし、AM4 ソケットの最後世代のRyzen 5000シリーズとともにいまだに販売されているという足の長い人気商品だ。
ただ、今回トランプ関税の影響で今後アメリカ国内におけるパソコン部品の値上がりが予想されたので、今のパソコンの調子が悪くなる前に新品を買って組んでおこうと予算1000ドルでNewEggから部品を調達してみた。 
以前はMaximum PCという月刊誌に推奨組み立てキットというか、 AMDとIntelのCPUにそれぞれ対応して、松竹梅の3種類のアセンブリリストが毎月掲載されており、これを参考にしながら組んでいたのだが、この雑誌今年の3月で休刊となってしまっていた。 
代わりに使ったのがNewEggのサイトでのAIアシスタント。 これでCPUをRyzenの 9000シリーズ(第9世代?)を指定し写真編集とProgrammingにも使えて予算1000ドルと入力してみて出てきたおすすめの部品構成を参考に注文してみた。
バンドル購入ということで500ギガバイトのM2PCE メモリがおまけについてきた。これは大きい。追加で2テラバイトのM2スティックを使い、ストレージ関連はSATAデバイスを追加すること無しでできてしまった。
ちなみに今回のMoboはGigagyteのB650 Aurus Elite というものだが M2の PCEスティックが3本まで装着できる。

というわけでざっくり値段をみると(NewEggの値段は毎日のように変わるのでその日によっておすすめも変わってくる)
1.CPU Ryzen 5 9600X $220
2.Mobo GigaByte B650 Auros Elite Ax $200
3.Case Corsair with cooling fans 3000D $80
4.SSD 2Tera Byte $120
SSD 500G Byte 無料
5.PSU module type 650W $100
6.GCU Radeon RX6600 $190
7.CPU Cooler $30
8.32GB Memory $90
計1050ドル これだけ買うと送料は無料になるわけだ。
要求の1000ドルに対して若干それをオーバーした品ぞろえをおすすめしてくるのはさすがにAIである。(実はMoboは同じグループでグレードがワンランク下の製品が150ドルなので、これを使えばちょうど1000ドルになる) でもせっかくおすすめしてくれたので従うことにする。

組んでみて、時代は変わったなあと思ったことをランダムに書いてみる。
1. ケースにDVDとかフロッピードライブを装着するベイがない。フロント面は全部Cooling Fan装着のためのスペースになってる。すいません。歳なのでソフトの導入はフロッピーやDVD/CDでやっていた世代なのです。ちなみに今5インチベイのついたPC用のケースを探すと(特に5インチベイX2などと指定すると)もう絶滅危惧種です。
2.ケースフロントからのコネクターにHDDアクセス表示用のLED配線がない。 当然LEDはついていない。 HDDやSDDはもう実装してないからこれでいいのか。ちなみにケースにスピーカはもちろんついていません。
3.MoboのFirmwareのアップデートはなんとCPUなしでも可能な機能が搭載されている。 これは他社のMoboでも同じような機能があるみたいだ。 このMoboはAM5ソケット用なのだが、印刷されたマニュアルを見ると7000シリーズとと8000シリーズのRyzenに対応となっている。 2024年のファームウエアでは9000シリーズにも対応しているのだが、購入したMoboのファームウェアが古いとそもそも起動しない恐れがあるわけで、そんな時CPUなしでもファームウエアが更新できるというのは必須の機能になるわけだ。ちなみに今回購入したMoboは9000シリーズに対応した初期のFirmwareが実装されていたので、この機能のお世話になることはなく、最新のファームウエアへのアップデートはBios画面から行った。
4.今やMoboにWifi機能がついているのは当たり前の時代になってしまった。
5.グラフィックカードにインテルのアーク A750 を推奨された。確かに低価格のグラフィックカードのベンチマークを見るとAMDやNVidiaに負けない(というか少し勝っているような)成績を出している。さすがにちょっとビビッてRadeonにしてしまいましたが、試してみればよかったかな。 ただ今回使ったRyzenはCPU自体にRadeonGraphicsがスタンダード実装されているのでこの低価格帯でのグラフィックスカードが本当に必要だったかどうかはちと微妙。カードを省けば800ドルで組めていた可能性はあります。
6. ARGBとRGBの違い。 今や3色LEDを自由に点灯できなければマザーボードとは言わないようで、ARGBという3ビンのコネクタ端子がいくつが実装されている。 B450-F実装されていたのは4ピンのRGBコネクターであって、それぞれ12V, R,G そしてB端子はそれぞれの色のグランド側だったわけだが、これで点灯させるLED群は1色しか指定できない。 なんの不思議もないと思っていたのだが、世の中進歩させたいという輩はまだまだいるのだ。 ARGBはプラス5ボルト、グランド、そしてデータという3端子構造になっており、 接続されているLED一個つづアドレス指定をして色の指定ができるということで、同じクーリングファンの照明にグラデーション効果などを持たせることができるようになっている。ということはたかだか3個入り15ドルでアマゾンから購入できるクーリングファンにはPwdモーターはもちろんのこと、LED用にはアドレスデコーダーチップとドライバーまでもが組み込んであるということで、いやはや中国製品のコスパはすごいわ、と改めて思った次第。 
7. CPUにクーラーが同梱されていなかった。 自分はオーバークロックなどはあまりしないので、CPUクーラーはCPU自体に同梱されてくる空冷式のクーラーを使って十分だったわけだが、今回購入のRyzenの9000シリーズのCPUにはクーラーがついていないのが標準みたいだ。 別途にクーラーだけで30ドルくらいの出費になるので、この分割高感がある。(ちなみにパッケージングの写真をみると8000シリーズまでの多くはクーラーが同梱されている模様)

ともかく、今回は配線のミスもなく(M2ソケットの使用など、ケーブルの取り回し量自体が以前に比べて減っているし、LEDのコネクターなども逆接続のバカ除けがきちんとしている。4PINのLEDコネクターは逆接続できてしまっていた)組んだ後の起動もまったく問題なし。
OSはWindows 11だが、Licenseは普通に買うと200ドルくらいする。 しかし世の中にはグレーマーケットというのがあって、PC生産のOEMが大量に確保したライセンスキーで「余ってしまった」キーを10ドルくらいで頒布しているサイトがある。 今回はGrouponというサイトを使ったが、一度インストールすると小売ライセンスのようにほかのマシーンにトランスファすることができない、という不便さを除けばちゃんと認識されてアクティブになる。

やねうらおうの定跡ファイルをサイトに使ってみた

ずーっと海外に住んでいて、将棋を指す相手がいなく、ヒカルの碁でネット碁の存在を知り、 こんなものがあるんだ、と検索して、Kurnickというゲームサイトを見つけ、そこでネット将棋を始めてみたら、みんな結構無茶苦茶な指し方をするのにびっくりして、英語の将棋入門サイトを作成したのが、今から17年くらい前、最初は静止画像をべたべたはりつけ、ブログに毛が生えたようなサイトだったが、フラッシュアプリを使った将棋盤というのが世の中にあるのがわかり、これを使って詰将棋やら最初の数手の説明、定跡の説明なんかをしていた。
割と早い段階で、フラッシュが駆逐されそうだということに気が付き、JavaScriptを勉強してフラッシュ無しでも動く将棋盤を自前で作成して自分のサイトで使い始めた。これがもうかれこれ12年以上前。これが幸いしてFlashが完全終息 した際のインパクトは避けることができた。

当時はそれなりに海外の将棋入門者の役にたったのではと思っているのだがそのうちYouTubeにHidetchさんが英語の将棋入門チャンネルを開き、さらに81道場を開設するにいたって、これは時代が変わったと悟った。 今ではほかのリソース、例えばYoutubeに上がる将棋の動画などもさらに充実し、DiscordではカロリーナのShogi Harborなど。 加えて自分もそろそろ終活を考慮しはじめるべき歳なのでこのサイト、もう閉じようかと思ったりするのだが、Statsを見るとまだ世界中からぼちぼちロボットではなく人間のアクセスがある。のでなんとなく続けている。 それにページを更新するごとにPHPの新しい使い方とかTypeScriptの勉強にもなるので、頭の老化防止にもつながっているような気がする。→どちらかというとこれが現在一番のモチベーションになっている。

最近何気にWikipediaの将棋の項目(英語版)を観たら外部リンクのリストの最初に自分のサイトが載っていて驚いた。ますます閉じにくくなった。

このサイトの中で何度か書き直しているページの中に、初めの数手の内容をまとめようとしたページがある。 最初はYamajunさんのサイトにある棋譜にリンクしていた。  しかしながら他のサイトへのリンクというのは実にあてにならないもので、 このリンクを含むリンク先の大部分が2019年時点で消失していた。 2009年当時Webの90%を占めていたFlash Player base のサイトが ほぼ全滅したのが大きいと思われる。
ならば自前でつくろうと考え、やり直しを重ねたこのページ。 一応形にはなったが、これは掘り下げていけばいくほど、もっと広げていきたい衝動に駆られる。

最近 やねうらお さんのGithubでやねうら王に使われている定跡の4年前のレベルのものが公開されているのに気が付いた。 自由にお使いくださいと書いてある。前にNHKの将棋講座の特集でだれかが将棋ソフトの作者さんたちは気前が良すぎるんですと言っていたが、まったくだ。  定跡フォーマットを見るとSFEN + move(s)の繰り返しのテキストファイルだ。 幸いSFENの読み込みと書き出し機能は自作のWeb用将棋盤表示アプリにすでに実装ずみなので、このファイルをそのまま使わせてもらってWebで定跡表示ができるのではないかと思いついた。

遠い昔 やねうらお さんというハンドルネームの方の、将棋界の内情を色々批評していたブログを見ていたようなきがするのだが、同一人物かどうかはさだかではない。 ただ、今や将棋プログラム界のドンとでもいえるような大活躍をされているようである。 5歳のときにTK‐80を使って8080のニーモニックを覚えたと書いているのを見ると、多分 自分より20歳くらい若い働き盛りなのでありましょう。自分がTK-80に触ったのはまだ日本にいたころ、取引先の業者がNECの代理店をやっていて、「遊んでみて」と持ってきたのが最初だ。モニターのルーチンを使ってスクロール表示をくるくる回しただけで満足して 終わったような気がする。

ともあれ、定跡ファイルをばらしてMySqlデータベースに落とし込んでみた。Pythonを覚える良い機会と思ったがコマンドラインでもPHPが使えることを思い出し、コードもあっさりかけてしまったので、Pythonの学習はまたもやお預けだ。(ファイルサイズが87メガバイトの大定跡ファイルは、MySQLに落とし込むのにPHPをコマンドラインで実行させて4~5時間ほどかかった。 専用サーバーではなく、他の業務と兼用なので、こんなものなのかもしれない)

局面数指し手数
やねうら王定跡(2016)130,182155,443
やねうら王大定跡(2016)428,2442,433,672
やねうら王テラショック【WCSC29】285,463512,860
各定跡ファイルの持つ局面数と指し手数

Web ServerにSFEN文字列を送るとと候補手のアレィをリスポンスとして返してくれるようなAPIを作成し、ページを追加してみた

我ながら GUI デザインのセンスが無いなあと思うのは毎度のことだが、 面白いなとおもったのは最初の定跡(説明によるとFloodGateで収集した棋譜を24手まで統計的にまとめたもの)の初手の頻度が人間の指す初手の頻度とほぼ一致していた点。2016年当時ということもあるのだろうが、ソフトというのは、もっと自由な手を指すのかと思っていた。この統計頻度の高い指し手を選び続けて行くと先手左美濃対後手四間飛車になる。 こういう風に指すんですねえ。
一方局面評価をしまくって精選したという100Tera Shock定跡なるデータファイルのほうでは評価値が高い指し手を選び続けると横歩取りに誘導される。

大定跡は一番充実しているはずなのだが、評価値の高い指し手を選んでいってもはやばやと定跡から外れるケースが多い。 原因は Mysqlへの落とし込みに失敗しているか、最初から定跡ファイルがそういう作りになっているかのどちらかだがよくわからない。ただ、下の表に示したように、初期局面1にたいし、二番目の(一手目)局面が14種類、三番目の局面(2手目)が56種類 と多くなっていくものの局面数が、指数関数的には広がっていないので結構枝をはらっているのはわかる。でも第一候補の次の指し手が無いというのはなんかおかしい。 暇ができたら見直してみます。

11
214
356
4135
5323
6678
71289
81922
92714
103623
114812
125995
137181
148407
159741
1611136
1712492
1814160
1915731
2017425
2118979
2220500
2321883
2423224
2524374
2625479
2726567
2827704
2928792
3029952
3131001
3231954

これだけの局面数と指し手を評価して枝をはらっていくというのは相当にリソースと時間が必要になると思うのだけど、それをgithub上にポンと公開してくれているのは 大丈夫ですか、と思ってしまう。恩恵にあずかっているわけですが。

Personal notes on Implementing autoPublishing mechanism to custom DB table within modx3.

A need.

  1. I have a custom database setup inside my modx3 install. Recently I started to think of adding a auto-publishing feature for this database, just as the modx3’s siteContent table is capable of doing.

Pre-requisite and assumptions.
1. MODX3 installation
2. Custom database table inside the MODX installation, which is created from modx database schema. I used migx’s package manager to create/modify one.
3. Let’s say that the class name of this dbtable is /MyDB/Model/Mydb
3. A custom table schema for modx to construct the table and associated code structure needs the following fields added to the schema. Note the data type for those date fields are all datetime. and not int(20) like original siteContent table fields for page resource. The difference will be dealt in the implementation code.

4. The auto-publish mechanism will mimic the existing code for modResource autopublishing mechanism. This reference code lives in core\src\revolution\modcachemanager.class.php. (for MODX2, the file is in core\model\modx\ folder)

5. The function I want is to toggle the record’s published flag based on records’ pub_date and un_pub date. It is still my implementation code (snippets) to filter records based on this flag to display data in the web site.

Two code segments have been written. The first is auto publishing functionality. The code heavily borrows from the aforementioned built-in autopublishing code, which is for page resources.

Some points to note.

  1. dbtype for these dates are datetime. In original code, they are integer(timestamp).   MySql function unix_timestamp() is used inside the query to  convert them as needed.
  2. The function return type is changed from array to void. I thus removed all return values construction as I could not think of any use case in my usage scenario.
  3. This code, when invoked will fetch the cache file /core/cache/my_db/auto_publish.cache.php. The file will return a timestamp value or 0 or null. The location of the cache file is defined in $options array and separate from the system’s auto publish cache file.
  4. if timestamp returned is less than current timestamp but not 0 or timestamp returned is null (meaning there is no cache file) then it proceeds to auto_publish function Otherwise it will return without doing anything.
    1. Auto publishing function will do the following. They are basically two pdo queries.
      1. Publish all record that’s pub_date is less than current time, unset pub_date, set publishedon to pub_date.
      2. Unpublish all record that’s unpub_date is less than current tme. unset unpub_date and unset publishedon date
      3. set new cache value to minimum of remaining unpub_date and pub_date sets.
    2. In the original code, Unpublishing query is resetting both unpub_date and pub_date. I can not see any side effects on not unsetting pub_date so I left it out.

Now, when to invoke this code? The original Resource auto_publishing code is invoked whenever resource is accessed.

I originally added the code as a snippet to my site’s landing page so that the code will execute whenever somebody visit my site. But I also wanted to tryout the plugin functionality, so I saved the code as a plugin and named the plugin refresh_pub_date

Then in the system’s settings, I created a custom event. myCustomeEvent and enabled the refresh_pub_date plugin to this event.

The following code will invoke this event. So my snippet in the landing page is just that.

Second code is used to check for pub_date, unpub_date after record save/update

This code will be invoked when Mydb record is added/updated. Since I use migxDB to create a CMP for this DB table in the manager, migxDB custom configurator allows the easy way to attach the snippet to after save action.

{"aftersave":"mySnippet"}

If called this way, migxDB provides $postvalues array variable to allow access to posted data field that is needed to be convert to timestamp like so.

It also provides $object variable for the class object (including record fields that are not posted) So the same value can be retrieved with usual get method.

Note: If I don’t use migxDB CMP configurator and write my own class processors extended from MODX processors, retrieval of the same value in aftersave hook in the update processor would have been

There was a bonus take away from this project

  1. I learned how to use MODX Cache file system. The main purpose of using the cache files must be obviously to eliminate a need of re-running of various codes on server side. However I can also do things like the following to record values of variables into the cache file for debugging purpose.

TV Japan から JMEへ

北米に住んで、長年にわたり親しんできたTVJapanがサービスを2024年3月31日で終了した。 NHKの番組をメインに、民放の番組もちりばめ、NHKの番組の合間にコマーシャルが入るという不思議な環境ではあったが、日本のニュースを見る時など、それなりに重宝していたし、日本のドラマなども楽しめていた。

時の流れというのは止められないもので、NetFlixやPrimeなどで日本のコンテンツもかなりの量が米国からみられるようになり、さらにNordVPNやら、ExpressVPNなどのVPNサービスを使ってあたかも日本からのアクセスしているように見せかければ、本来は海外からは見られないようなコンテンツもこれらのサービスで視聴可能。こういう術に長けたものにとっては従来のTVサービスの重要性は失われていくものなのだろう。

TVJAPANの場合、終了のお知らせは多分2か月くらい前からはじまり、かなり急な話ではあった。 それに代わるサービスとしてJMEというストリーミングサービスが新たに始まった。 3つの24時間チャンネル。 さらに人気ドラマやバラエティなど選ばれたメディアソフトの配信サービスということで、かなり充実しているはずなのだが、TVJapanを見なれてきたものからするとかなり不便に感じる。

何故か。 録画予約ができないんである。

考えてみれば当たり前のことで、今までも録画予約機能はTVJapanが提供していたわけではなく、 TVJapanのチャンネルを供給していたケーブルサービスのDVR機能を使っていたわけだ。 だからJME自体に録画機能がないと文句をいうのはお門違いなわけだが、やっぱり不便である。

もちろんいいこともあって、月額の料金は5ドル下がったし、そもそもTVJapanを入れるためにはケーブルサービスのTVバンドルでのプレミアパッケージを購入しなければいけなかったので、これを解約してベーシックパッケージにしたら、ケーブル局への支払いも60ドル下がったんである。 よって、TAX入れれば月70ドル近い節約になり、なんでこんな金額を今まで払っていたのか、と思わなくもないわけだ。

チャンネルの一つNHKワールドプレミアムは日本のNHKの配信をそのまま流している。もう一つのNHKワールドは外国人向けのNHK放送なので英語が主体。 三つ目のチャンネルはJMEセレクトで、今のところ、同じ番組の組み合わせを6時間ごとに毎日4回放映している。JMEセレクトはニュース7以外のニュースは番組表に入っていないので、ワールドプレミアムを見るしかないのだが何せニュース番組なので見るチャンスが一回しかない。 以前は録画して後でも見れたのだが、今は時間を合わせてみるしかない。

AmazonのFireStickをTVに差し込んでみている場合はFireStickの8GBのメモリーを使って同時録画は可能。

PCやアンドロイド、あるいはIOSデバイスでのストリーミングサービスの録画というのは検索すればいくらでも出てくるが基本的には画面キャプチャーの機能だから、その場での”録画”はいくらでもできる。 ただし、録画予約ということになると、デバイスは最初からオンにして放映しっぱなしの状態での時間を指定してそこからの録画というのはできるとして、JMEアプリを起動してチャンネルを合わせ、キャプチャー開始となると、そんな機能のアプリってあったっけ、という話になる。 やろうと思えば、自分でも作れなくはないという気はするが、APIのとか認証のやり取りの解析の手間を考えるとそこまでのモチベーションはない。

一番良いのは例えばHULUのliveTV で用意されているCloud DVR on HULUなどのような機能をJMEが追加してくれることだと思うが、そういう風に進化してくれないかな?

大昔、まだVCRなんてものが無かった時代は6時から始まる「○○○○○○」が見たくて、大急ぎで部活を切り上げ自宅に戻っていたなんてことがあったようななかったような。 そんな時代に逆戻りしたような感覚。

一方、On demandの部分、見始めて話が面白いと「不適切にもほどがある」をまとめて全話みてしまう(BINGE Watchというらしい)という暴挙をついやってしまうという健康上非常に不適切な行為におよんでしまい、 配信サービスに対する距離感を決めておかないと非常にまずい、と思い始めた今日この頃です。

array_reduceの話2

array_reduceと arrayのsplat オペレーターを使うと、同じアレーを生成するという意味のない関数ができる。

で、パターンを使って、array_mapもarray_filterもarray_reduceで代用できるというお話

めでたし、めでたし

array_reduceの話

PHPでプログラミングするとき、例えば、以下のようなアレィがあったとする

この内容をキーとともにリストアップしたいとき、まず思いつくのがforeachを使ってこう書く方法

ただ、このアレィの複数エレメントを一つの値(この場合は一つの文字列)にまとめるという風に考えるとarray_reduceという関数を使っても同じことができる

この関数、単純なArray処理は問題ないとして、Associative Arrayを処理するとなると、はたと困るが、渡すArrayを値ではなく、キーを渡すことで可能となる。これは一つ目の因数にarray_keysを使うことで実現できる。コールバックには因数が二つ付くが、慣習として$curry と$itemという変数名を使う。for 文に$iをつかっているのと同様で、もちろんほかの変数名としても問題ない。$curryは結果を集積していく入れ物。$itemはアレィの各エレメント。上の例ではコールバック関数はアローファンクションを使ってインラインの書き込み。 三つ目の因数($output)は初期値

このarray_reduceの使いかたをネットの検索などで探すと、数字を加算していくような例がよく紹介されているが、上のように文字列アレィの加工にも有効。 さらに、このアレィは関数のアレィでもよいわけで、例えば、複数の関数で同じ文字列の処理を繰り返すような場合、例えば、文字列をトリムしあとhtmlにエンコードしたい場合など、つまり同じ文字列に何度もパスをかけて加工したい場合など

$output=trim($output);
$output=htmlspecialchars($output);
//または
$output=htmlspecialchars(trim($output));

のかわりに

$output= array_reduce([trim,htmlspecialchars], fn($curry,$item)=>$item($curry),$output)

と書いてしまう技がある。関数が二つなら最初のアプローチのほうがよさそうだが、かかわる関数が増えてくると、コードのメンテナンスは下のパターンのほうがすっきりするよ、とFunctional Programmingの推進者たちはいっているが、さてどうする。

$functionsArray=[fn1,fn2,fn3,fn4,fn5]; //同じオブジェクトに対し、5つの関数処理をおこないたい
$result=array_reduce($functionArray, fn($curry,$item)=>$item($curry),$initialValue);

“Why did I not think of this?” moment in MODX 3 snippet development with PHPStorm

This is one of those ‘Aha!’ moments.

MODX is one of CMSs (content management system) that I have been using for a long time to maintain my shogi site. Market penetration of this CMS is said to be 0.1%. As of this writing, 43% of of Web sites uses WordPress and no other CMS reach even 10% of market share. (source, w3techs.com . I actually think MODX penetration is much greater than this number, MODX does not leave big footprint to show it’s identity in front of web crawlers. -Many sites I know powered by MODX, including mine – were identified as non-CMS site by w3tech’s tool.— Nevertheless, there is no doubt WordPress dominates the CMS market)

Although both uses same language (PHP) for powering the site, MODX gives much more flexibility in site design but you need to be proficient with HTML and CSS (and JavaScript). In another word, MODX is a tool while WordPress is a product.

What I liked about MODX over WordPress at the time was a separation of PHP code and HTML. This was a long time ago so the situation might have changed on WordPress, but I have been happy with MODX since.

At the time, I also looked at Drupal and Joomra . All of them forces you to follow their interpretation of what website should be. MODX has no opinion on how you want to create Web site. You have a total freedom.

On the other hand, if the only thing you want to do with your site is blogging, there is no better solution than using WordPress.

I digressed. Back to the subject!

With a wealth of plug-ins available, your rarely need to code in PHP. When you do though, MODX separates a PHP coding part from HTML with a thing called Snippets. A snippet is then inserted in to HTML with a use of tag.

[[aSnippet]]

You can edit a snippet within MODX backend’s editor. With a plugin like ‘ACE’ there is a nominal amount of syntax checking to the PHP code. Unfortunately, I am being spoiled with convenience of IDE such as PHPStorm,

MODX backend, Snippet editor with ACE plugin

So there is a desire to be able to write a snippet code using PHPStorm and get benefits of auto-completion and methods hinting, strict error checking and warning.

Unfortunately, there is no plugin in PHPStorm for MODX or MODX plugn for PHPStorm.

When I google “MODX with PHPStorm”, there are several hits, Although none of them gave me a direct answer on what I wanted to do, I was able to put enough facts together and came up with a solution. The solution that was just waiting for me to assemble few known facts, that I have used all of them separately in the past!


1. Fact that PHPstorm can create a project using existing remote site contents, so that PHPStorm can pull all file setup from MODX site.

PHPStorm->File->New Project from Existing Files


2. Fact that PHPstorm can then start syncing file contents of my local project files to server files.

File sync options available in PHPStorm

Ok, but Snippet is stored in Database. the code is not in the file, but then,

3. Fact, MODX snippet code can be from a static file, meaning I can create a PHP file and feed it as a snippet to MODX by turning on “is Static” option and point to the file location

Is Static option is turned on for Snippet. Snippet is now fed from “static file”



Result: When I combine those 3 factors, I can create a snippet with a comfort of PHPStorm IDE environment.

Snippet now editable in PHPStorm
Snippet output on browser page, powered through MODX

What do I gain? Full context support and code hinting, as PHPStorm analyzes whole MODX site and figures out all variables and object and its methods being used inside MODX. I have to give credit to MODX development team for fully documenting source files. Thank you!

PHPstorm explains what getOption method is
PHPStorm autocomplete support in action.

You can also open the corresponding file by pointing to method and do control-b to drill down on function.

I edit a file in PHPStorm, and you either 1) turn on auto-sync between your local files and server files or 2) manually upload changed file to server. Then I can run and test the snippet with MODX backend immediately.

So all of the sudden, I feel like invincible. It will be very hard for me to make coding error. (Ok, logic error still possible)

Once Snippet development is done, you may switch off the ‘is static’ option and keep the snippet inside database, remove the php file from the system.

One thing that is kind of annoying is that by default, PHPstorm thinks $modx variable is undefined. The default setting on PHPStorm’s inspections on undefined variable is somewhat on conservative side. It will not even acknowledge variables from included file!. To mitigate this, you need to put a check on “Search for variable’s definition outside the current file”

PHP settings->Editor->Inspection->PHP->Undefined symbols->Undefined variable

It hurts to think that I have been using both PHPstorm and MODX for more than 10 years and I am figuring this out just now 🙁

Creating a Blog page inside MODX version 3 web site

MODX comes clean slate so there is no ‘ready to use’ provisions for creating Blog style site. One has to set it up using tools available in the CMS.

I took an easy way out for my production site by creating blog site using free wordPress service and then feed content to MODX page. with this, the only extra you will need is Spiefeed to handle rss feed from WordPress.  But for purist approach it is certainly possible to setup blog section within MODX using few extras.

There are several articles available for creating Blog sites using MODX.  The one shown in the modx official site is somewhat dated, but the article actually have a link to the instruction/article of more recent implementation of MODX blog site.  Link is here.   Better yet, this instruction is accompanied with a package published in the github which has all chunks, a snippet and a template packaged up to duplicate the blog site that the article discusses.  A demo site is also available to check out the feel of using this template. The document states that the demo utilizes MODX 2.7.  So can I use this setup with MODX 3 site? Furthermore, the instruction assumes you want the Site to be a blogging site. Can I add ‘Blog page’ to already existing MODX site? Answer to both questions are ‘Yes’ but we need to modify some parts.

To start, I could have created each chunk and template manually following the instruction but to reduce my typing effort, I went ahead and downloaded the demo package. I also installed dependent extras (getResources, getPage, Collections, and Tagger).  Previously, I did not have any of those installed. – I am using pdoTools  to get resources and get page. – <edit> It was fairly easy to change references to getResources and getPage to equivalent pdo* snippets.

Again, the setup instruction is written for MODX version 2.7 and the site I am setting up this blog structure is version 3.0.3.  Setup also assumes the top page is a site home page. There are several area in chunks and template that refers to [[++site_start]] and this need to be changed to point to correct Resource.  I want to have a blog page as a subpage of the site.

Looking through the template and snippets that I setup with the downloaded package , I found some more modifications were needed.

I will list out those changes.

  1. My server uses Nginx and it needs additional html tag to specify base url in <head> section of blog template html. Without it, Friendly URL won’t work correctly.
    <base href="[[++site_url]]">
  2. In MODX 3, [[*class_key]] does not return “modResource”.  It returns “MODX\Revolution\modResource”.
    This means I get “MODX/Revolution\modDocument” or “MODX\Revolution\CollectionContainer” in [[*class_key]] field depending on resource selected. Two sections of “Blog Template” stops working due to this.

    1. Template tries to pull in “jumbotron_CollectionContainer” or “jumbotron chunk based on [[$jumbotron-[[]*class_key]]] tag. This tag doesn’t work in MODX 3 as it returns [[$jumbotron-MODX/Revolution/….]] which does not exist and forward slash within chunk name is probably invalid anyway.
    2. Template tries to insert [[$blog_listing]] or [[$blog_article]] based on conditional modifier [[*id:is`[[++site_start]]`:then….]] but the blog_listing page is not at [[+site_start]] resources in my use case.
      1. To mitigate both problems, I have created a system setting [[++blog_start]] in custom namespace/area and set it to id of a blog home page resource I created. Then rewrote tags as follows.
        For jumbotron section
        [[[[*id:is=`[[++blog_start]]`:then=`$jumbotron-CollectionContainer`:else`$jumbotron-modDocument`]]]]
        For container section
        [[[[*id:is=`[[++blog_start]]`:then=`$blog_listing`:else=`$blog_article`]]]]
      2. Note: It will be probably not that difficult to create snippets and replace conditional tags here.
  3. getResources and getSnippet tags are used to aggregate child resources and parents properties are set to site home page [[++site_start]].   These needed to be changed to aforementioned [[++blog_start]].  These changes had to be made on $blog_listing chunk and $jumbotron-CollectionContainer chunk.
  4. @groups properties are hardcoded in calls to TaggerGetTags snippet.  The snippet is called from $aside chunk is setting @groups to 1 and the snippet called from $headder chunk is setting @groups to 2.  Make sure this agrees with your tagger group settings. I have first created ‘Category’ group and then ‘Archives’ group so I needed to change them to 2 and 1 respectively.
  5. Last and least 🙂   as this is just a demo template, links to “subscribe”, “search”, “Sign up”, “GitHub”, “Twitter” and “Facebook” are all mocked and pointing to itself.  You either need to remove or put actual link to them when modifying to suit your site design.

So, in order to create a Blog section within Modx 3 hosted website, using this instruction and elements in the github repo, One needs to be aware of:

  1. Modx version 3’s breaking change of the model class caused decision logics inside Blog_Template to stop working.  Rework of the logic flow was needed.
  2. Friendly url.  Nginx needs base href reference in head section.
  3. Creating a blog section other than home page location. Needed to change references to [[++site_start]] system settings to [[++blog_start]], a user created system setting
  4. Adjust the template design to fit with other area of site design.

Some more miscellaneous items to note.

  1. $blog_listing_item chunk, content placeholder has a modifier :firstp. There is a firstp snippet that is installed with the package.  I opted to replace it with ellipsis=`500` modifier to limit the content output to first 500 chars followed by ellipsis to suit my preference.
  2. Also in $blog_listing, getPage snippet uses @blog properties set. This set does not exists so the default property set will be used instead. I think idea is to create a blog property set within getPage snippet to override the default property settings in easy way, if customization is needed.

Commenting on the posts are not possible.  This need to be setup by yourself and will require Quip extra. that part is not covered by this article. One has to go back to modx documentation’s tutorial section.